/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.web.api;

import cn.jfast.framework.web.core.ApplicationContext;
import cn.jfast.framework.web.aop.AopHandler;
import cn.jfast.framework.web.view.View;
import cn.jfast.framework.web.view.ViewDriver;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Api动作流程控制
 */
public class ApiInvocation {

	/** 被访问Api类对象 */
	private final Object targetApi;
	/** 拦截器链 */
	private final List<AopHandler> aopList;
	/** 包装后的Api对象 */
	private final Api api;
	/** http请求 */
	private final HttpServletRequest request;
	/** http响应 */
	private final HttpServletResponse response;
	/** Api返回视图 */
	private Object view;
	/** 视图驱动器 */
	private final ViewDriver viewDriver;
	/** Api方法反射类 */
	private final ApiInvoker invoke;
	/** Api执行链游标 */
	private int cur = 0;
	/** 执行长度标志 */
	private int STACK_SIZE = 0;
	/** Api路径 */
	private String route;
	/** 异常记录 */
	private List<Exception> ex = new ArrayList<Exception>();

	public Object getView() {
		return view;
	}

	public void setView(View view) {
		this.view = view;
	}

	public ApiInvocation(Api api,
						 Object object,
						 HttpServletRequest request,
						 HttpServletResponse response,
						 String route){
		this.api = api;
		this.targetApi = ApplicationContext.fillResource(object);
		this.aopList = api.getAopList();
		this.STACK_SIZE = this.aopList.size();
		this.request = request;
		this.response = response;
		this.viewDriver = new ViewDriver();
		this.route = route;
		invoke = new ApiInvoker();
		invoke.setTargetMethod(api.getMethod());
		invoke.setTargetObject(targetApi);
		invoke.setTargetClass(api.getApi());
		invoke.setRequest(request);
		invoke.setResponse(response);
		invoke.setRoute(route);
		for(int i = 0; i< aopList.size(); i++){
			aopList.set(i, (AopHandler) ApplicationContext.fillResource(aopList.get(i)));
		}
	}

	public Object getBean(){
		return targetApi;
	}

	public Method getMethod(){
		return api.getMethod();
	}

	public String getRoute(){
		return route;
	}

	public void invoke() { 
		try {
			if(cur < STACK_SIZE){
				aopList.get(cur++).beforeHandle(this, request, response,ex);
			} else if(cur == STACK_SIZE){
				cur++;view = invoke.invoke();invoke();
			} else if(cur <= 2 * STACK_SIZE){
				aopList.get(2 * STACK_SIZE - cur++).afterHandle(this, request, response,ex);
			}
		} catch (Exception ex) {
			this.ex.add(ex);invoke();
		} finally {
			if(cur == 2 * STACK_SIZE+1 || !ex.isEmpty()) {
				cur = 9999999;
				viewDriver.render(view, request, response, ex);
				ex = Collections.emptyList();
			}
		}

	}
}
